﻿using Artech.Common;
using Artech.Core.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ContainerModel.Unity;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Unity.Utility;
using System.Collections.Generic;

namespace Artech.Core
{
    /// <summary>
    /// The factory to create unity container.
    /// </summary>
    public  class UnityContainerBuilder
    {
        private static UnityContainerBuilder current;
        private UnityContainerBuilder()
        {}
        /// <summary>
        /// Gets the current <see cref="UnityContainerBuilder"/>.
        /// </summary>
        /// <value>
        /// The current <see cref="UnityContainerBuilder"/>.
        /// </value>
        public static UnityContainerBuilder Current
        {

            get { return current ?? (current = new UnityContainerBuilder()); }
        }

        /// <summary>
        /// Creates the default unity container based on the given configuration name.
        /// </summary>
        /// <param name="unityContainerName">Configuration name of the unity container.</param>
        /// <returns>The unity container.</returns>
        public IUnityContainer CreateDefaultUnityContainer(string unityContainerName = "")
        {
            return CreateDefaultUnityContainer(ConfigurationSourceManager.Current.ConfigurationSource, unityContainerName);
        }

        /// <summary>
        /// Creates the default unity container.
        /// </summary>
        /// <param name="configurationSource">The configuration source.</param>
        /// <param name="unityContainerName">Configuration name of the unity container.</param>
        /// <returns>The unity container.</returns>
        public IUnityContainer CreateDefaultUnityContainer(IConfigurationSource configurationSource, string unityContainerName = "")
        {
            Guard.ArgumentNotNull(configurationSource, "configurationSource");
            IUnityContainer container = InternalCreateDefaultUnityContainer(configurationSource, unityContainerName);
            UnityContainerConfigurator configurator = new UnityContainerConfigurator(container);
            IContainerReconfiguringEventSource reconfiguringEventSource = (IContainerReconfiguringEventSource)configurator;
            ITypeRegistrationsProvider typeRegistrationsProvider = new CompositeTypeRegistrationsProviderLocator(GetConfigurationSectionLocators());
            configurator.RegisterAll(configurationSource, typeRegistrationsProvider);
            return container;
        }

        /// <summary>
        /// Gets all of built-in <see cref="ConfigSectionLocator"/>.
        /// </summary>
        /// <returns>The <see cref="ConfigSectionLocator"/> list.</returns>
        public IEnumerable<ITypeRegistrationsProvider> GetConfigurationSectionLocators()
        {
            yield return new ConfigSectionLocator(Constants.ConfigurationSections.Codes);
            yield return new ConfigSectionLocator(Constants.ConfigurationSections.Messages);
            yield return new ConfigSectionLocator(Constants.ConfigurationSections.ApplicationContext);
            yield return new ConfigSectionLocator(Constants.ConfigurationSections.Caching);
            yield return new ConfigSectionLocator(Constants.ConfigurationSections.Resources);
            yield return new ConfigSectionLocator(Constants.ConfigurationSections.Globalization);
        }

        internal IUnityContainer InternalCreateDefaultUnityContainer(IConfigurationSource configurationSource, string unityContainerName = "")
        {
            IUnityContainer container = new UnityContainer();
            UnityConfigurationSection unityConfigSection = configurationSource.GetSection(UnityConfigurationSection.SectionName) as UnityConfigurationSection;
           
            if (null != unityConfigSection)
            {
                if( unityConfigSection.Containers.Default != null && string.IsNullOrWhiteSpace(unityContainerName))
                {
                    unityConfigSection.Configure(container);
                }
                if (!string.IsNullOrWhiteSpace(unityContainerName))
                {
                    unityConfigSection.Configure(container, unityContainerName);
                }
            }

            UnityContainerConfigurator configurator = new UnityContainerConfigurator(container);
            IContainerReconfiguringEventSource reconfiguringEventSource = (IContainerReconfiguringEventSource)configurator;
            List<ITypeRegistrationsProvider> typeRegistrationsProviders = new List<ITypeRegistrationsProvider>();
            IEnumerable<ITypeRegistrationsProvider> builtInProviders = ConfigurationBasedTypeRegistrationsProviderFactory.CreateTypeRegistrationsProviderLocators(configurationSource, reconfiguringEventSource);
            typeRegistrationsProviders.AddRange(builtInProviders);

            ServiceLocationSettings settings = ServiceLocationSettings.GetSettings(configurationSource);
            if (null == settings)
            {
                typeRegistrationsProviders.Add(new DefaultServiceLocatorTypeRegistrationsProvider());
            }
            else
            {
                typeRegistrationsProviders.Add(new ConfigSectionLocator(Constants.ConfigurationSections.ServiceLocation));
            }

            ITypeRegistrationsProvider typeRegistrationsProvider = new CompositeTypeRegistrationsProviderLocator(typeRegistrationsProviders);
            configurator.RegisterAll(configurationSource, typeRegistrationsProvider);
            return container;
        }
    }
}